﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Reflection;

namespace Jayden.Dll.XmlScript.Descriptions
{
	public class XmlObjectDescription : XmlExpressionDescription
	{
		public static readonly XmlObjectDescription Instance = new XmlObjectDescription();

		public XmlExpression Load(XmlScriptLoader loader, XmlElement element)
		{
			if (element == null)
				throw new NullReferenceException("element cannot be null");
			XmlAttribute typeAttribute = element.Attributes["type"];
			if (typeAttribute == null)
				return new XmlObject(LoadPrimitive(element));
			Type type = Type.GetType(typeAttribute.Value);
			if (type == null)
				throw new Exception(string.Format("unknow type : '{0}'", typeAttribute.Value));
			object instance = Activator.CreateInstance(type);
			foreach(XmlNode node in element.ChildNodes)
				if (node is XmlElement)
				{
					XmlElement subElement = node as XmlElement;
					XmlAttribute nameAttribute = subElement.Attributes["name"];
					if (nameAttribute != null)
					{
						PropertyInfo property = type.GetProperty(nameAttribute.Value);
						if (property != null)
							property.SetValue(instance, Load(loader, subElement), null);
					}
				}
			return new XmlObject(instance);
		}
		public object LoadPrimitive(XmlElement element)
		{
			if (element == null)
				throw new NullReferenceException("element cannot be null");
			XmlAttribute valueAttribute = element.Attributes["value"];
			bool haveValue = (valueAttribute != null);
			switch (element.Name)
			{
				case "byte":
					return haveValue ? byte.Parse(valueAttribute.Value) : default(byte);
				case "long":
					return haveValue ? long.Parse(valueAttribute.Value) : default(long);
				case "ulong":
					return haveValue ? ulong.Parse(valueAttribute.Value) : default(ulong);
				case "int":
					return haveValue ? int.Parse(valueAttribute.Value) : default(int);
				case "uint":
					return haveValue ? uint.Parse(valueAttribute.Value) : default(uint);
				case "short":
					return haveValue ? short.Parse(valueAttribute.Value) : default(short);
				case "ushort":
					return haveValue ? ushort.Parse(valueAttribute.Value) : default(ushort);
				case "double":
					return haveValue ? double.Parse(valueAttribute.Value) : default(double);
				case "float":
					return haveValue ? float.Parse(valueAttribute.Value) : default(float);
				case "decimal":
					return haveValue ? decimal.Parse(valueAttribute.Value) : default(decimal);
				case "bool":
					return haveValue ? bool.Parse(valueAttribute.Value) : default(bool);
				case "string":
					return haveValue ? valueAttribute.Value : default(string);
			}
			throw new Exception(string.Format("unknow primitive name : '{0}'", element.Name));
		}
		public bool IsToken(XmlElement element)
		{
			if (element == null)
				return false;
			switch (element.Name)
			{
				case "byte":
				case "long":
				case "ulong":
				case "int":
				case "uint":
				case "short":
				case "ushort":
				case "double":
				case "float":
				case "decimal":
				case "bool":
				case "string":
					return true;
			}
			XmlAttribute typeAttribute = element.Attributes["type"];
			return typeAttribute != null && !string.IsNullOrWhiteSpace(typeAttribute.Value);
		}
	}
}
